package jps;


import java.util.ArrayList;
import java.util.List;

public class JPFMoveDiagonallyIfNoObstacles extends JumpPointFinderBase {

    public JPFMoveDiagonallyIfNoObstacles(int startX, int startY, int endX, int endY, Grid grid) {
        super(startX, startY, endX, endY, grid);
    }

    public int[] jump(int x, int y, int px, int py) {
        if (!super.grid.isWalkableAt(x, y)) {
            return null;
        }
        if (super.grid.getNodeAt(x, y) == super.endNode) {
            return new int[] { x, y };
        }
        int dx = x - px;
        int dy = y - py;
        // check for forced neighbors
        // along the diagonal
        if (dx != 0 && dy != 0) {
            // when moving diagonally, must check for vertical/horizontal jump points
            if (this.jump(x + dx, y, x, y) != null || this.jump(x, y + dy, x, y) != null) {
                return new int[] { x, y };
            }
        }
        // horizontally/vertically
        else {
            if (dx != 0) {
                if ((super.grid.isWalkableAt(x, y - 1) && !super.grid.isWalkableAt(x - dx, y - 1))
                        || (super.grid.isWalkableAt(x, y + 1) && !super.grid.isWalkableAt(x - dx, y + 1))) {
                    return new int[] { x, y };
                }
            } else if (dy != 0) {
                if ((super.grid.isWalkableAt(x - 1, y) && !super.grid.isWalkableAt(x - 1, y - dy))
                        || (super.grid.isWalkableAt(x + 1, y) && !super.grid.isWalkableAt(x + 1, y - dy))) {
                    return new int[] { x, y };
                }
            }
        }
        // moving diagonally, must make sure one of the vertical/horizontal
        // neighbors is open to allow the path
        if (super.grid.isWalkableAt(x + dx, y) && super.grid.isWalkableAt(x, y + dy)) {
            return this.jump(x + dx, y + dy, x, y);
        } else {
            return null;
        }
    }

    protected List<int[]> findNeighbors(Node node) {
        List<int[]> neighbors = new ArrayList<>();
        Node parent = node.getParent();
        // directed pruning: can ignore most neighbors, unless forced.
        if (parent != null) {
            int px = parent.getX();
            int py = parent.getY();
            int x = node.getX();
            int y = node.getY();
            // get the normalized direction of travel
            int dx = (x - px) / Math.max(Math.abs(x - px), 1);
            int dy = (y - py) / Math.max(Math.abs(y - py), 1);
            // search diagonally
            if (dx != 0 && dy != 0) {
                if (super.grid.isWalkableAt(x, y + dy)) {
                    neighbors.add(new int[] { x, y + dy });
                }
                if (super.grid.isWalkableAt(x + dx, y)) {
                    neighbors.add(new int[] { x + dx, y });
                }
                if (super.grid.isWalkableAt(x, y + dy) && super.grid.isWalkableAt(x + dx, y)) {
                    neighbors.add(new int[] { x + dx, y + dy });
                }
            }
            // search horizontally/vertically
            else {
                boolean isNextWalkable;
                if (dx != 0) {
                    isNextWalkable = super.grid.isWalkableAt(x + dx, y);
                    boolean isTopWalkable = super.grid.isWalkableAt(x, y + 1);
                    boolean isBottomWalkable = super.grid.isWalkableAt(x, y - 1);
                    if (isNextWalkable) {
                        neighbors.add(new int[] { x + dx, y });
                        if (isTopWalkable) {
                            neighbors.add(new int[] { x + dx, y + 1 });
                        }
                        if (isBottomWalkable) {
                            neighbors.add(new int[] { x + dx, y - 1 });
                        }
                    }
                    if (isTopWalkable) {
                        neighbors.add(new int[] { x, y + 1 });
                    }
                    if (isBottomWalkable) {
                        neighbors.add(new int[] { x, y - 1 });
                    }
                } else if (dy != 0) {
                    isNextWalkable = super.grid.isWalkableAt(x, y + dy);
                    boolean isRightWalkable = super.grid.isWalkableAt(x + 1, y);
                    boolean isLeftWalkable = super.grid.isWalkableAt(x - 1, y);
                    if (isNextWalkable) {
                        neighbors.add(new int[] { x, y + dy });
                        if (isRightWalkable) {
                            neighbors.add(new int[] { x + 1, y + dy });
                        }
                        if (isLeftWalkable) {
                            neighbors.add(new int[] { x - 1, y + dy });
                        }
                    }
                    if (isRightWalkable) {
                        neighbors.add(new int[] { x + 1, y });
                    }
                    if (isLeftWalkable) {
                        neighbors.add(new int[] { x - 1, y });
                    }
                }
            }
        }
        // return all neighbors
        else {
            List<Node> neighborNodes = super.grid.getNeighbors(node);
            for (int i = 0, l = neighborNodes.size(); i < l; ++i) {
                Node neighborNode = neighborNodes.get(i);
                neighbors.add(new int[] { neighborNode.getX(), neighborNode.getY() });
            }
        }
        return neighbors;
    }

}
